﻿// SKNotes - a note taking and gathering tool
// Copyright (C) 2009  Stefan Kueng
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

namespace SKNotes
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Threading;
    using SKNotes.Configuration;
    using SKNotes.CustomControls;
    using SKNotes.CustomControls.TaskDialog;
    using SKNotes.Dialogs;
    using SKNotes.Utilities;

    /// <summary>
    /// Interaction logic for ApplicationWindow.xaml
    /// </summary>
    public partial class ApplicationWindow : BaseWindow
    {
        private BackgroundWorker worker = new BackgroundWorker();
        private BackgroundScanner scanner = new BackgroundScanner();
        private Point dragStart;
        private object dragObject;
        private ListViewItem dragLvi;
        private TreeViewItem dragTvi;
        private TreeViewItem lastDragOverTvi;
        private DateTime lastDragOvertime;
        private DeferredAction deferredFilter;


        public ApplicationWindow()
        {
            DataContext = NoteList.Instance;
            InitializeComponent();
            WindowSettings.Settings.Add(new ListViewSetting(noteListView));


            // filter everything out until something is selected in the tree view
            ICollectionView view = CollectionViewSource.GetDefaultView(this.noteListView.ItemsSource);
            if (view != null)
            {
                view.Filter = (obj) => false;
            }

            this.worker.DoWork += new DoWorkEventHandler(this.scanner.DoWork);
            this.worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(AppWindow_Close);
            this.worker.RunWorkerAsync();

            this.Activated += delegate
            {
                Dispatcher.BeginInvoke(DispatcherPriority.Input, (Action)delegate
                {
                    noteViewer.SetFocus();
                });
            };
            this.noteListView.SelectionChanged += delegate
            {
                Dispatcher.BeginInvoke(DispatcherPriority.Input, (Action)delegate
                {
                    noteViewer.SetFocus();
                });
            };
        }

        void AppWindow_Close(object sender, RunWorkerCompletedEventArgs e)
        {
            // background worker has finished, close the application
            this.Close();
        }

        private void AppWindow_Closing(object sender, CancelEventArgs e)
        {
            this.worker.CancelAsync();
            this.scanner.Signal();
            e.Cancel = this.worker.IsBusy;
        }

        private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (noteViewer != null)
            {
                noteViewer.Note = noteListView.SelectedItem as Note;
            }
        }

        private void RefreshFilter()
        {
            ICollectionView view = CollectionViewSource.GetDefaultView(this.noteListView.ItemsSource);

            if (view != null)
            {
                NoteTag tag = mainTree.SelectedItem as NoteTag;
                NoteBook book = mainTree.SelectedItem as NoteBook;

                HashSet<string> searchResult = new HashSet<string>();
                if ((!string.IsNullOrEmpty(searchBox.Text)) || ((tag != null) && (!string.IsNullOrEmpty(tag.SearchString))))
                {
                    Lucene.Net.Analysis.Standard.StandardAnalyzer analyzer = new Lucene.Net.Analysis.Standard.StandardAnalyzer();
                    string[] fields = { "content", "tag", "title", "inline", "attachment" };
                    Lucene.Net.QueryParsers.MultiFieldQueryParser parser = new Lucene.Net.QueryParsers.MultiFieldQueryParser(fields, analyzer);
                    Lucene.Net.Search.Query query = null;
                    try
                    {
                        parser.SetDefaultOperator(Lucene.Net.QueryParsers.QueryParser.AND_OPERATOR);
                        if ((tag != null) && (!string.IsNullOrEmpty(tag.SearchString)))
                        {
                            query = parser.Parse(tag.SearchString);
                        }
                        else
                        {
                            query = parser.Parse(searchBox.Text);
                        }
                    }
                    catch (Exception e)
                    {
                        System.Diagnostics.Debug.Print(e.Message);
                    }

                    Lucene.Net.Search.Hits hits = null;
                    try
                    {
                        Lucene.Net.Search.IndexSearcher searcher = new Lucene.Net.Search.IndexSearcher(NoteList.GetNotebookDirPath());

                        hits = searcher.Search(query);
                    }
                    catch (Exception e)
                    {
                        System.Diagnostics.Debug.Print(e.Message);
                    }

                    for (int i = 0; i < hits.Length(); i++)
                    {
                        Lucene.Net.Documents.Document doc = hits.Doc(i);
                        Lucene.Net.Documents.Field field = doc.GetField("id");
                        if (field != null)
                        {
                            string s = field.StringValue();
                            if (!string.IsNullOrEmpty(s))
                                searchResult.Add(s);
                        }
                    }
                }
                else
                    searchResult = null;

                if (tag != null)
                {
                    // first mark the current notebook
                    foreach (NoteBook b in NoteList.Instance.Tags)
                    {
                        if (b == tag.NoteBook)
                        {
                            NoteList.Instance.CurrentNotebook = b;
                            break;
                        }
                    }

                    view.Filter = (obj) =>
                        {
                            bool include = ((Note)obj).NoteBook == NoteList.Instance.CurrentNotebook;
                            if ((string.IsNullOrEmpty(tag.SearchString)) || (tag.SearchString.Length == 0))
                            {
                                include = include && NoteList.FindTag(((Note)obj).Tags, tag);
                            }
                            include = include && ((searchResult == null) || (searchResult.Contains(((Note)obj).ID)));
                            return include;
                        };
                }
                if (book != null)
                {
                    view.Filter = (obj) =>
                        {
                            bool include = ((Note)obj).NoteBook == book;
                            include = include && ((searchResult == null) || (searchResult.Contains(((Note)obj).ID)));
                            return include;
                        };
                    NoteList.Instance.CurrentNotebook = book;
                }
            }
        }

        private void MainTree_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            NoteTag tag = mainTree.SelectedItem as NoteTag;
            if (tag != null)
            {
                if (tag.NoteBook != null)
                    NoteList.Instance.CurrentNotebook = tag.NoteBook;
            }
            this.RefreshFilter();
        }

        private void MainTree_Loaded(object sender, RoutedEventArgs e)
        {
            DependencyObject depObject = mainTree
                            .ItemContainerGenerator
                            .ContainerFromIndex(0);
            if (depObject != null)
            {
                ((TreeViewItem)depObject).IsSelected = true;
            }
        }

        private void CommandExecuteAlways(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

        private void CommandExecuteIfListItemSelected(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (noteListView.SelectedItem != null);
        }

        private void CommandExecuteIfTreeItemSelected(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (mainTree.SelectedItem != null);
        }

        private void NoteListView_SelectTag(object sender, ExecutedRoutedEventArgs e)
        {
            TagSelector tagSelectorDlg = new TagSelector();
            tagSelectorDlg.Owner = this;
            Nullable<bool> dialogResult = tagSelectorDlg.ShowDialog();
            if ((dialogResult != null) && (dialogResult == true))
            {
                Note o = noteListView.SelectedItem as Note;
                if (o != null)
                {
                    o.AddTag(new NoteTag(tagSelectorDlg.textBox.Text, o.NoteBook));
                }
            }
        }

        private void NoteListView_EditTitle(object sender, ExecutedRoutedEventArgs e)
        {
            object o = noteListView.SelectedItem;
            ListViewItem lvi = (ListViewItem)noteListView.ItemContainerGenerator.ContainerFromItem(o);
            EditBox eb = (EditBox)UIHelper.GetFrameworkElementByName<EditBox>(lvi, "EditBox");
            if (eb != null)
            {
                eb.IsEditing = true;
            }
        }

        private void NoteListView_NewNote(object sender, ExecutedRoutedEventArgs e)
        {
            noteViewer.Note = null;
            noteViewer.SetFocus();
            noteListView.SelectedItem = null;
        }

        private void NoteListView_DeleteNote(object sender, ExecutedRoutedEventArgs e)
        {
            Note note = noteListView.SelectedItem as Note;
            if (note != null)
            {
                if (note.NoteBook.NoteBookName.Equals(TrashFolder.TrashName, StringComparison.Ordinal))
                {
                    // the note is already in the trash bin, delete it completely
                    TaskDialog dialog = new TaskDialog();
                    dialog.HeaderIcon = TaskDialogIcon.Question;
                    dialog.Header = string.Format("Delete Note \"{0}\"", note.Name);
                    dialog.TaskDialogButton = TaskDialogButton.Custom;
                    dialog.DefaultResult = TaskDialogResult.Button2;
                    dialog.SystemSound = TaskDialogSound.Question;
                    dialog.HeaderBackground = Brushes.White;
                    dialog.HeaderForeground = Brushes.Navy;
                    dialog.Content = "Do you really want to delete the Note?";
                    dialog.Detail = "Deleting notes from the trash bin will delete the notes permanently. This action can not be undone!";
                    dialog.Button1Text = "Delete";
                    dialog.Button2Text = "Cancel";
                    dialog.ParentWindow = this;
                    TaskDialogResult res = dialog.Show();
                    if (res == TaskDialogResult.Button1)
                    {
                        NoteList.Instance.DeleteNote(note);
                    }
                }
                else
                {
                    // the note is not yet in the trash bin - move it to the trash bin for now
                    NoteList.Instance.MoveNoteToTrash(note);
                }
            }

            this.RefreshFilter();
        }

        private void MainTree_NewNotebook(object sender, ExecutedRoutedEventArgs e)
        {
            NewTagDialog newTagDlg = new NewTagDialog();
            newTagDlg.Owner = this;
            Nullable<bool> dialogResult = newTagDlg.ShowDialog();
            if ((dialogResult != null) && (dialogResult == true))
            {
                NoteList.Instance.CreateNotebook(newTagDlg.tagName.Text);
            }
        }

        private void MainTree_NewTag(object sender, ExecutedRoutedEventArgs e)
        {
            NoteBook book = mainTree.SelectedItem as NoteBook;
            NoteTag tag = mainTree.SelectedItem as NoteTag;

            NewTagDialog newTagDlg = new NewTagDialog();
            newTagDlg.Owner = this;
            Nullable<bool> dialogResult = newTagDlg.ShowDialog();
            if ((dialogResult != null) && (dialogResult == true))
            {
                if (book != null)
                {
                    NoteTag newTag = new NoteTag(newTagDlg.tagName.Text, book);
                    if (!book.AddTag(newTag))
                    {
                        newTag = null;
                    }
                }
                else if (tag != null)
                {
                    NoteTag newTag = new NoteTag(tag.TagPath + ":" + newTagDlg.tagName.Text, tag.NoteBook);
                    if (!NoteList.AddTag(tag.NoteBook.Tags, newTag))
                    {
                        newTag = null;
                    }
                }
            }
        }

        private void MainTree_NewSavedSearch(object sender, ExecutedRoutedEventArgs e)
        {
            NoteBook book = mainTree.SelectedItem as NoteBook;
            NoteTag tag = mainTree.SelectedItem as NoteTag;

            NewSavedSearch newSSDlg = new NewSavedSearch();
            newSSDlg.Owner = this;
            newSSDlg.searchString.Text = this.searchBox.Text;
            if ((tag != null) && (tag.IsSearch))
                newSSDlg.searchString.Text = tag.SearchString;
            Nullable<bool> dialogResult = newSSDlg.ShowDialog();
            if ((dialogResult != null) && (dialogResult == true))
            {
                if (book != null)
                {
                    NoteTag newTag = new NoteTag(newSSDlg.tagName.Text, book);
                    newTag.SearchString = newSSDlg.searchString.Text;
                    if (!book.AddTag(newTag))
                    {
                        newTag = null;
                    }
                }
                else if (tag != null)
                {
                    NoteTag newTag = new NoteTag(tag.TagPath + ":" + newSSDlg.tagName.Text, tag.NoteBook);
                    newTag.SearchString = newSSDlg.searchString.Text;
                    if (!NoteList.AddTag(tag.NoteBook.Tags, newTag))
                    {
                        newTag = null;
                    }
                }
            }
        }

        private void MainTree_EditSavedSearch(object sender, ExecutedRoutedEventArgs e)
        {
            NoteTag tag = mainTree.SelectedItem as NoteTag;
            if ((tag == null) || (tag.IsSearch == false))
                return;

            EditSavedSearch editSSDlg = new EditSavedSearch();
            editSSDlg.Owner = this;
            editSSDlg.tagName.Content = tag.TagName;
            editSSDlg.searchString.Text = tag.SearchString;
            Nullable<bool> dialogResult = editSSDlg.ShowDialog();
            if ((dialogResult != null) && (dialogResult == true))
            {
                tag.SearchString = editSSDlg.searchString.Text;
            }
        }


        private void MainTree_DeleteTag(object sender, ExecutedRoutedEventArgs e)
        {
            NoteBook book = mainTree.SelectedItem as NoteBook;
            NoteTag tag = mainTree.SelectedItem as NoteTag;

            // deleting a tag doesn't mean the notes are gone too, so we don't ask
            // for confirmation first.
            // when deleting a notebook, all notes are moved to the trash bin first,
            // so no confirmation dialog there either.
            if (tag != null)
            {
                NoteList.Instance.RemoveTag(tag);
            }
        }

        private void MainTree_RenameTag(object sender, ExecutedRoutedEventArgs e)
        {
            TreeViewItem tvi = TreeViewExtensions.ContainerFromItem(mainTree, mainTree.SelectedItem);
            EditBox eb = (EditBox)UIHelper.GetFrameworkElementByName<EditBox>(tvi, "EditBox");
            if (eb != null)
            {
                eb.IsEditing = true;
            }
        }

        private void MainTree_EmptyTrash(object sender, ExecutedRoutedEventArgs e)
        {
            List<Note> notesToDelete = new List<Note>();
            foreach (Note n in NoteList.Instance.Notes)
            {
                if (n.NoteBook.GetType() == typeof(TrashFolder))
                {
                    notesToDelete.Add(n);
                }
            }

            // no remove all notes
            foreach (Note n in notesToDelete)
            {
                NoteList.Instance.DeleteNote(n);
            }
        }

        private void TreeViewItem_PreviewMouseRightButtonDown(object sender, MouseEventArgs e)
        {
            TreeViewItem item = sender as TreeViewItem;
            if (item != null)
            {
                item.IsSelected = true;
            }
        }

        private void MainTree_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            mainTree.Focus();
        }

        private void MainTree_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount != 1)
            {
                return;
            }

            NoteTag tag = mainTree.SelectedItem as NoteTag;
            if (tag == null)
                return;

            this.dragTvi = TreeViewExtensions.ContainerFromItem(mainTree, mainTree.SelectedItem);

            if (this.dragTvi == null)
            {
                return;
            }

            this.dragObject = tag;
            this.dragStart = MouseUtilities.GetMousePosition(this.dragTvi);
        }

        private void MainTree_MouseMove(object sender, MouseEventArgs e)
        {
            if (((this.dragTvi == null) || (this.dragObject == null)) || (e.LeftButton != MouseButtonState.Pressed))
            {
                return;
            }
            Point pt;
            try
            {
                pt = MouseUtilities.GetMousePosition(this.dragTvi);
            }
            catch (System.InvalidOperationException)
            {
                return; // this visual is not connected to a presentation source
            }

            bool horzGesture = Math.Abs(pt.X - this.dragStart.X) > SystemParameters.MinimumHorizontalDragDistance;
            bool vertGesture = Math.Abs(pt.Y - this.dragStart.Y) > SystemParameters.MinimumVerticalDragDistance;

            NoteTag dragTag = mainTree.SelectedItem as NoteTag;
            if ((horzGesture || vertGesture) && (dragTag != null))
            {
                DragDropEffects allowedEffects = DragDropEffects.Copy | DragDropEffects.Move;
                DragDrop.DoDragDrop(this.noteListView, dragTag, allowedEffects);
                this.dragObject = null;
            }
        }

        private void NoteListView_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            noteListView.Focus();
        }

        private void NoteListView_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount != 1)
            {
                return;
            }

            // If the item at the selected index is null, we have nothing to drag, so just return
            if (noteListView.SelectedItem == null)
            {
                return;
            }

            if (IsMouseOverListScrollbar())
                return;

            this.dragLvi = (ListViewItem)noteListView.ItemContainerGenerator.ContainerFromItem(noteListView.SelectedItem);
            if (this.dragLvi == null)
            {
                return;
            }

            this.dragObject = this.noteListView.SelectedItem;
            this.dragStart = MouseUtilities.GetMousePosition(this.dragLvi);
        }

        private void NoteListView_MouseMove(object sender, MouseEventArgs e)
        {
            if (((this.dragLvi == null) || (this.dragObject == null)) || (e.LeftButton != MouseButtonState.Pressed))
            {
                return;
            }
            Point pt;
            try
            {
                pt = MouseUtilities.GetMousePosition(this.dragLvi);
            }
            catch (System.InvalidOperationException)
            {
                return; // this visual is not connected to a presentation source
            }

            bool horzGesture = Math.Abs(pt.X - this.dragStart.X) > SystemParameters.MinimumHorizontalDragDistance;
            bool vertGesture = Math.Abs(pt.Y - this.dragStart.Y) > SystemParameters.MinimumVerticalDragDistance;

            Note dragNote = this.noteListView.SelectedItem as Note;
            if ((horzGesture || vertGesture) && (dragNote != null))
            {
                DragDropEffects allowedEffects = DragDropEffects.Copy | DragDropEffects.Move;
                DragDrop.DoDragDrop(this.noteListView, dragNote, allowedEffects);
                this.dragObject = null;
            }
        }

        private void MainTree_DragOver(object sender, DragEventArgs e)
        {
            e.Effects = DragDropEffects.None;
            if (e.Data.GetDataPresent(typeof(Note)))
            {
                Note note = e.Data.GetData(typeof(Note)) as Note;

                // find the tree view item which is the potential drop target
                HitTestResult hitTestResults = VisualTreeHelper.HitTest(mainTree, e.GetPosition(mainTree));
                DependencyObject o = hitTestResults.VisualHit;
                while (o != null && o.GetType() != typeof(TreeViewItem))
                {
                    o = VisualTreeHelper.GetParent(o);
                }

                TreeViewItem tvi = o as TreeViewItem;
                if (tvi != null)
                {
                    // we have a drop target - allow the drop
                    e.Effects = DragDropEffects.Copy;
                    NoteTag tag = tvi.DataContext as NoteTag;
                    NoteBook book = tvi.DataContext as NoteBook;
                    TrashFolder trash = tvi.DataContext as TrashFolder;
                    if (trash != null)
                    {
                        // items can only be moved to the trash, not copied
                        e.Effects = DragDropEffects.Move;
                    }

                    // find out if we have to expand the tree node
                    if (this.lastDragOverTvi == tvi)
                    {
                        if (this.lastDragOvertime != null)
                        {
                            TimeSpan span = DateTime.Now - this.lastDragOvertime;
                            if ((span > SystemParameters.MouseHoverTime) && (!tvi.IsExpanded))
                            {
                                tvi.IsExpanded = true;
                            }
                        }
                    }
                    else
                    {
                        this.lastDragOverTvi = tvi;
                        this.lastDragOvertime = DateTime.Now;
                    }
                }
            }
            else if (e.Data.GetDataPresent(typeof(NoteTag)))
            {
                NoteTag dragNoteTag = e.Data.GetData(typeof(NoteTag)) as NoteTag;

                // find the tree view item which is the potential drop target
                HitTestResult hitTestResults = VisualTreeHelper.HitTest(mainTree, e.GetPosition(mainTree));
                DependencyObject o = hitTestResults.VisualHit;
                while (o != null && o.GetType() != typeof(TreeViewItem))
                {
                    o = VisualTreeHelper.GetParent(o);
                }

                TreeViewItem tvi = o as TreeViewItem;
                if (tvi != null)
                {
                    // we have a drop target - allow the drop
                    e.Effects = DragDropEffects.Move;
                    NoteTag tag = tvi.DataContext as NoteTag;
                    NoteBook book = tvi.DataContext as NoteBook;
                    if (tag != null)
                    {
                        if (tag.NoteBook != dragNoteTag.NoteBook)
                        {
                            e.Effects = DragDropEffects.None;
                        }
                    }
                    if (book != null)
                    {
                        e.Effects = DragDropEffects.None;
                    }

                    // find out if we have to expand the tree node
                    if (this.lastDragOverTvi == tvi)
                    {
                        if (this.lastDragOvertime != null)
                        {
                            TimeSpan span = DateTime.Now - this.lastDragOvertime;
                            if ((span > SystemParameters.MouseHoverTime) && (!tvi.IsExpanded))
                            {
                                tvi.IsExpanded = true;
                            }
                        }
                    }
                    else
                    {
                        this.lastDragOverTvi = tvi;
                        this.lastDragOvertime = DateTime.Now;
                    }
                }
            }

            e.Handled = true;
        }

        private void MainTree_Drop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(typeof(Note)))
            {
                // find the tree view item which is the potential drop target
                HitTestResult hitTestResults = VisualTreeHelper.HitTest(mainTree, e.GetPosition(mainTree));
                DependencyObject o = hitTestResults.VisualHit;
                while (o != null && o.GetType() != typeof(TreeViewItem))
                {
                    o = VisualTreeHelper.GetParent(o);
                }

                TreeViewItem tvi = o as TreeViewItem;
                if (tvi != null)
                {
                    // we have a drop target, select the item
                    NoteTag tag = tvi.DataContext as NoteTag;
                    NoteBook book = tvi.DataContext as NoteBook;
                    TrashFolder trash = tvi.DataContext as TrashFolder;
                    Note note = e.Data.GetData(typeof(Note)) as Note;
                    if (note != null)
                    {
                        if (tag != null)
                        {
                            if (tag.NoteBook != note.NoteBook)
                            {
                                Note newNote = NoteList.Instance.CopyNoteToNotebook(note, tag.NoteBook);
                                newNote.AddTag(tag);
                            }
                            else
                            {
                                // add the tag to the note
                                note.AddTag(tag);
                            }
                        }
                        else if (trash != null)
                        {
                            // delete the note
                            NoteList.Instance.MoveNoteToTrash(note);
                        }
                        else if (book != null)
                        {
                            NoteList.Instance.CopyNoteToNotebook(note, book);
                        }
                    }
                }
            }
            else if (e.Data.GetDataPresent(typeof(NoteTag)))
            {
                // find the tree view item which is the potential drop target
                HitTestResult hitTestResults = VisualTreeHelper.HitTest(mainTree, e.GetPosition(mainTree));
                DependencyObject o = hitTestResults.VisualHit;
                while (o != null && o.GetType() != typeof(TreeViewItem))
                {
                    o = VisualTreeHelper.GetParent(o);
                }

                TreeViewItem tvi = o as TreeViewItem;
                if (tvi != null)
                {
                    // we have a drop target, select the item
                    NoteTag tag = tvi.DataContext as NoteTag;
                    TrashFolder trash = tvi.DataContext as TrashFolder;
                    NoteTag dragNoteTag = e.Data.GetData(typeof(NoteTag)) as NoteTag;
                    if (dragNoteTag != null)
                    {
                        if (tag != null)
                        {
                            bool lowerHalf = false;
                            Point pt = MouseUtilities.GetMousePosition(tvi);
                            Grid grid = (Grid)VisualTreeHelper.GetChild(tvi, 0);
                            if (grid != null)
                            {
                                Border bd = (Border)VisualTreeHelper.GetChild(grid, 1);
                                if (bd != null)
                                {
                                    if (pt.Y > (bd.ActualHeight / 2))
                                    {
                                        lowerHalf = true;
                                    }
                                }
                            }

                            NoteList.Instance.MoveTag(dragNoteTag, tag, !lowerHalf);
                        }
                        if (trash != null)
                        {
                            NoteList.Instance.RemoveTag(dragNoteTag);
                        }
                    }
                }
            }

            e.Handled = true;
        }

        private void searchBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (this.deferredFilter == null)
            {
                this.deferredFilter = DeferredAction.Create(() => this.RefreshFilter());
            }

            this.deferredFilter.Defer(new TimeSpan(0, 0, 0, 0, 500));
        }

        /// <summary>
        /// Returns true if the mouse cursor is over a scrollbar in the ListView.
        /// </summary>
        bool IsMouseOverListScrollbar()
        {
            Point ptMouse = MouseUtilities.GetMousePosition(this.noteListView);
            HitTestResult res = VisualTreeHelper.HitTest(this.noteListView, ptMouse);
            if (res == null)
                return false;

            DependencyObject depObj = res.VisualHit;
            while (depObj != null)
            {
                if (depObj is System.Windows.Controls.Primitives.ScrollBar)
                    return true;

                // VisualTreeHelper works with objects of type Visual or Visual3D.
                // If the current object is not derived from Visual or Visual3D,
                // then use the LogicalTreeHelper to find the parent element.
                if (depObj is Visual || depObj is System.Windows.Media.Media3D.Visual3D)
                    depObj = VisualTreeHelper.GetParent(depObj);
                else
                    depObj = LogicalTreeHelper.GetParent(depObj);
            }

            return false;
        }
    }
}
